Infinite Scroll - 1
#Base Function
-
getList
- 5개의 게시글을 a비동기적으로 불러오게 되는 함수
-
renderItem
- 하나의 게시글을 DOM 요소로 변경하는 함수
-
fetchMore
-
$footer 에 loading 을 해준후
-
비동기로 list를 불러옴
-
documentFragment 를 이용하여 $app 요소까지 부착하게 되는 작업
-
loading 표시를 제거해줌
-
#Main Function
- 기본적으로 window에 scroll시에 onScroll 을 작동하게 합니다.
- clientHeight 는 element의 내부 높이입니다.
- 내부 여백(padding) 을 포함하고, 수평 스크롤바의 높이, 경계선 외부 여백(margin)을 포합하지 않습니다.
- scrollTop 은 element 최상단과 보여지는 컨텐츠와의 거리를 의미합니다.
- 세로 스크롤가 없다면 scrollTop 은 항상 0이 됩니다
- ScrollHeight 는 보이지 않는 부분까지 전체의 높이를 나타냅니다
- 결론적으로 스크롤이 제일 바닥으로 내려온 상태가 되면 clientHeight + scrollTop = ScrollHeight 임으로 게시글을 5개 더 불러오게 됩니다.
#Issue
-
scroll 이 발생할때 마다 onScroll이 동작하고 계산을 하게 됩니다.
- 이벤트의 발생 횟수를 줄이는 방법을 생각해봅니다
- 디바운스와 쓰로틀링
-
최적화된 Web API 인 Intersection Observer 를 사용하여 개선할 수 있습니다.
-
타겟 요소와 상위요소 또는 최상의 document의 사이의 intersection내의 변화를 관찰합니다
-
<div id="app"></div>
<div class="footer"></div>
const $app = document.querySelector('#app')
const $footer = document.querySelector('.footer')
const getList = (count) => {
return new Promise(resolve => {
setTimeout(() => {
const data = Array.from({length:5}).map((_,idx)=>{
const id = (count*5)+idx+1
return {id, data: `${id}번째 게시글입니다`}
})
resolve(data)
},1000)
})
}
const renderItem = ({id, data}) => {
const item = document.createElement('li')
item.innerHTML = `
<div class="item-id">${id}</div>
<div class="item-data">${data}</div>
`
return item
}
let count = 0
const fetchMore = async () => {
$footer.classList.add("loading");
const list = await getList(count ++)
const frag = document.createDocumentFragment()
list.forEach(item=> {
frag.appendChild(renderItem(item))
})
$app.appendChild(frag)
$footer.classList.remove("loading");
}
const onScroll = (e) => {
const {clientHeight,scrollTop,scrollHeight} = e.target.scrollingElement;
if (clientHeight + scrollTop === scrollHeight) {
fetchMore()
}
}
fetchMore()
window.addEventListener('scroll',onScroll)
.footer {
height : 50px;
line-height: 50px;
text-align:center;
}
#app li {
display:flex;
margin: 10px;
border:1px solid black;
height : 20vh;
text-align :center;
line-height: 20vh
}
.item-id {
border-right: 1px solid black;
width : 20%;
}
.item-data {
width : 80%;
}
.loading:after {
content: "... 로딩중 ...";
}